﻿//----------------------------------------------------------------------------
// NFC Orchestra
// An NFC demo app for Windows Phone 8+
// Copyright (C) Andreas Jakl and Rina Ahmed
// https://nfcorchestra.codeplex.com/
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.
//----------------------------------------------------------------------------
using System;
using System.IO.IsolatedStorage;
using System.Linq;
using System.Runtime.InteropServices.WindowsRuntime;
using System.Text;
using System.Threading;
using System.Windows;
using System.Windows.Media.Animation;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using Windows.Networking.Proximity;
using Windows.Phone.Speech.Synthesis;
using Microsoft.Phone.Shell;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using NdefLibrary.Ndef;
using NfcOrchestra.Resources;

namespace NfcOrchestra
{
    public partial class MainPage
    {
        private ProximityDevice _device;
        private readonly byte[] _nfcMusicType = Encoding.UTF8.GetBytes("msday.at:music");
        private SpeechSynthesizer _speech;
        private readonly IsolatedStorageSettings _settings;
        private const string SettingsUnlockedKey = "Unlocked";
        private const char NfcMessageUnlockCode = 'U';
        private const char NfcMessageOkCode = 'O';

        private bool _initialized;
        private long _subscribedMessageId = 0;
        private long _publishedMessageId = 0;

        // Constructor
        public MainPage()
        {
            InitializeComponent();
            BuildLocalizedApplicationBar();

            _initialized = false;

            _settings = IsolatedStorageSettings.ApplicationSettings;
            SetUnlockedInstrumentImage();
        }

        protected override void OnNavigatedTo(NavigationEventArgs e)
        {
            base.OnNavigatedTo(e);

            ThreadPool.QueueUserWorkItem(func => Initialize());
        }

        protected override void OnNavigatedFrom(NavigationEventArgs e)
        {
            base.OnNavigatedFrom(e);
            if (_device != null)
            {
                if (_subscribedMessageId != 0)
                {
                    _device.StopSubscribingForMessage(_subscribedMessageId);
                    _subscribedMessageId = 0;
                }
                if (_publishedMessageId != 0)
                {
                    _device.StopPublishingMessage(_publishedMessageId);
                    _publishedMessageId = 0;
                }
            }
            _initialized = false;
        }

        private void Initialize()
        {
            if (_initialized) return;

            try
            {
                _device = ProximityDevice.GetDefault();
                if (_device == null)
                {
                    ShowMessageBox(AppResources.StatusNfcErrorText, AppResources.StatusNfcErrorTitle);
                    return;
                }
                _subscribedMessageId = _device.SubscribeForMessage("NDEF", MessageReceivedHandler);

                _speech = new SpeechSynthesizer();
            }
            catch (Exception ex)
            {
                ShowMessageBox(String.Format(AppResources.ErrorInitText, ex.Message), AppResources.ErrorInitTitle);
            }

            _initialized = true;
        }

        private void MessageReceivedHandler(ProximityDevice sender, ProximityMessage message)
        {
            // Parse raw byte array to NDEF message
            var rawMsg = message.Data.ToArray();
            var ndefMessage = NdefMessage.FromByteArray(rawMsg);

            // Loop over all records contained in the NDEF message
            foreach (NdefRecord record in ndefMessage)
            {
                if (record.TypeNameFormat == NdefRecord.TypeNameFormatType.ExternalRtd &&
                    record.Type.SequenceEqual(_nfcMusicType))
                {
                    switch (record.Payload[0])
                    {
                        case (byte)NfcMessageUnlockCode:
                            // "U" = unlock instrument, send answer
                            SendUnlockMessage(NfcMessageOkCode);
                            ShowUnlockedMessage();
                            break;
                        case (byte)'O':
                            // "O" = OK, received answer
                            ShowUnlockedMessage();
                            break;
                        default:
                            // Anything else - play instrument / voice
                            PlayInstrument(record.Payload);
                            break;
                    }
                }
            }
        }
        private void ShowUnlockedMessage()
        {
            Dispatcher.BeginInvoke(() =>
            {
                _settings[SettingsUnlockedKey] = true;
                _settings.Save();
                SetUnlockedInstrumentImage();
                ShowMessageBox(AppResources.UnlockedGuitarText, AppResources.UnlockedGuitarTitle);
            });
        }

        private bool IsGuitarUnlocked()
        {
            return (_settings != null && 
                _settings.Contains(SettingsUnlockedKey) &&
                    _settings[SettingsUnlockedKey] is bool &&
                    (bool)_settings[SettingsUnlockedKey]);
        }

        private void SetUnlockedInstrumentImage()
        {
            if (!IsGuitarUnlocked()) return;
            ImgGuitar.Source = new BitmapImage
            {
                UriSource =
                    new Uri(
                        ((bool)_settings[SettingsUnlockedKey])
                        ? "/Assets/Instruments/guitar_unlocked.png"
                        : "Assets/Instruments/guitar.png", UriKind.Relative)
            };

        }

        private void PlayInstrument(byte[] payload)
        {
            if (!_initialized)
            {
                ShowMessageBox(AppResources.StatusNotInitializedTitle, AppResources.StatusNotInitializedText);
                return;
            }
            var instrument = payload[0] - 48;
            if (instrument == 3) 
            {
                // Speech!
                // Payload: 3Hallo Welt!
                Dispatcher.BeginInvoke(async () =>
                {
                    await _speech.SpeakTextAsync(
                        Encoding.UTF8.GetString(payload, 1, payload.Length - 1));
                });
                StartAnimation(instrument);
                return;
            }

            if (instrument == 2 && !_settings.Contains(SettingsUnlockedKey)) 
            {
                ShowMessageBox("Unlock this instrument by clicking unlock and tapping your friend's phone with the app running!", "Instrument Locked");
                return;
            }

            var soundfiles = new[]
            {
                "Assets/Sounds/beat.wav",   // Creative Commons Public Domain Dedication http://freesound.org/people/thecluegeek/sounds/156370/
                "Assets/Sounds/sax.wav",    // Creative Commons Attribution-NonCommercial 3.0 Unported http://freesound.org/people/Timbre/sounds/90236/
                "Assets/Sounds/guitar.wav"  // Creative Commons Attribution 3.0 Unported http://freesound.org/people/Carrik_Caser/sounds/134769/
            };

            var stream = TitleContainer.OpenStream(soundfiles[instrument]);
            var effect = SoundEffect.FromStream(stream);
            FrameworkDispatcher.Update();
            effect.Play();
            StartAnimation(instrument);
        }

        private void StartAnimation(int number)
        {
            var animations = new[]
            {
                "BgBeat",
                "BgSax",
                "BgGuitar",
                "BgVoice"
            };
            Dispatcher.BeginInvoke(() =>
            {
                FadeoutStoryboard.Stop();
                Storyboard.SetTargetName(FadeoutStoryboard, animations[number]);
                FadeoutStoryboard.Begin();
            });
        }

        

        private void MessageTransmittedHandler(ProximityDevice sender, long messageId)
        {
            // This method is called when unlocking the instrument 
            _device.StopPublishingMessage(messageId);
            _publishedMessageId = 0;
        }

        private void BtnUnlock_Click(object sender, RoutedEventArgs e)
        {
            if (IsGuitarUnlocked())
            {
                // Guitar is already unlocked
                ShowMessageBox(AppResources.UnlockGuitarAlreadyText, AppResources.UnlockGuitarTitle);
            }
            else
            {
                SendUnlockMessage(NfcMessageUnlockCode);
                ShowMessageBox(AppResources.UnlockGuitarText, AppResources.UnlockGuitarTitle);
            }
        }

        private void SendUnlockMessage(char code)
        {
            if (!_initialized || _device == null)
            {
                ShowMessageBox(AppResources.StatusNotInitializedTitle, AppResources.StatusNotInitializedText);
                return;
            }
            if (_publishedMessageId != 0)
                return;
            var record = new NdefRecord
            {
                TypeNameFormat = NdefRecord.TypeNameFormatType.ExternalRtd,
                Type = _nfcMusicType,
                Payload = new[] {(byte)code}
            };
            var msg = new NdefMessage { record };
            _publishedMessageId = _device.PublishBinaryMessage("NDEF", msg.ToByteArray().AsBuffer(),
                MessageTransmittedHandler);
        }

        private void ShowMessageBox(string text, string title)
        {
            Dispatcher.BeginInvoke(() => MessageBox.Show(text, title, MessageBoxButton.OK));
        }

        private void CmdInstructions(object sender, EventArgs e)
        {
            NavigationService.Navigate(new Uri("/InstructionsPage.xaml", UriKind.Relative));
        }

        private void CmdAbout(object sender, EventArgs e)
        {
            NavigationService.Navigate(new Uri("/AboutPage.xaml", UriKind.Relative));
        }

        private void BtnSetup_Click(object sender, RoutedEventArgs e)
        {
            NavigationService.Navigate(new Uri("/SetupPage.xaml", UriKind.Relative));
        }

        private void BuildLocalizedApplicationBar()
        {
            // Set the page's ApplicationBar to a new instance of ApplicationBar.
            ApplicationBar = new ApplicationBar { Mode = ApplicationBarMode.Minimized };

            // Menu Items

            // Instructions
            var appBarInstructionsMenu = new ApplicationBarMenuItem(AppResources.AppBarInstructionsText);
            appBarInstructionsMenu.Click += CmdInstructions;
            ApplicationBar.MenuItems.Add(appBarInstructionsMenu);

            // About
            var appBarAboutMenu = new ApplicationBarMenuItem(AppResources.AppBarAboutText);
            appBarAboutMenu.Click += CmdAbout;
            ApplicationBar.MenuItems.Add(appBarAboutMenu);
        }


        private void ImgInstrument_Tap(object sender, System.Windows.Input.GestureEventArgs e)
        {
            ShowMessageBox(AppResources.InstrumentTapInfoMsgText, AppResources.InstrumentTapInfoMsgTitle);
        }

    }
}